package com.terra.ns.imp.common.mybatis.ext

import com.baomidou.mybatisplus.core.enums.SqlMethod
import com.baomidou.mybatisplus.core.injector.AbstractMethod
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo
import com.baomidou.mybatisplus.core.metadata.TableInfo
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils
import org.apache.ibatis.mapping.MappedStatement
import java.util.stream.Collectors

/**
@author qins
@date 2023/5/31
@desc mp自带的update会将为null的列过滤掉不更新，即时有几种策略也满足不了场景，有时候我们需要将该列更新为null值
@param predicate  字段筛选条件
 */
class UpdateFieldIncludeNull(private val predicate: ((t: TableFieldInfo) -> Boolean)? = null): AbstractMethod("updateFieldIncludeNull") {

    override fun injectMappedStatement(
        mapperClass: Class<*>,
        modelClass: Class<*>,
        tableInfo: TableInfo
    ): MappedStatement {
        // 拼接sql
        val sqlMethod = SqlMethod.UPDATE
        val sql = String.format(
            sqlMethod.sql, tableInfo.tableName,
            sqlSet(tableInfo),
            sqlWhereEntityWrapper(true, tableInfo), sqlComment()
        )
        val sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass)
        return this.addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource)
    }

    /**
     * 自定义 set 语句sql
     */
    private fun sqlSet(
        table: TableInfo
    ): String {
        var fieldList = table.fieldList
        // 过滤忽略字段
        if(predicate != null) {
            fieldList =  fieldList.stream().filter(predicate).collect(Collectors.toList())
        }
        val sqlScript = fieldList.joinToString(NEWLINE) {
            field -> getIfSet(field)
        } + NEWLINE
        return SqlScriptUtils.convertSet(sqlScript)
    }

    /**
     * 因为有忽略列表，所以拼接一层条件
     */
    private fun getIfSet(field: TableFieldInfo): String {
        // 得到 column=#{xxx},
        val sqlSet = field.column + EQUALS + SqlScriptUtils.safeParam(ENTITY_DOT + field.el) + COMMA
        // 拼上 <if test="!list.contains('type')">type=#{et.type},</if>  list为忽略列表
        return SqlScriptUtils.convertIf(sqlSet, String.format("!list.contains('%s')", field.property), false)
    }

}